# -*- coding: utf-8 -*-
# @Time    : 2019/4/2 16:55
# @Author  : zhouqiang
# @Site    : 
# @File    : brinson.py
# @Software: PyCharm

"""
股票组合brinson归因，后续这个脚本逐渐淘汰
已跟渊叔确认，该接口目前没有使用
已将该脚本注释，等渊叔那边最终确认后，该脚本和接口删除
"""

import math
import json
import pandas as pd
from quant_researcher.quant.project_tool import hammer
from quant_researcher.quant.project_tool.logger.my_logger import LOG
from quant_researcher.quant.project_tool.slim import sys_exit
from quant_researcher.quant.datasource_fetch.portfolio_api.funcs_to_delete import get_all_big, \
    compute_portfolio_nav_and_chg
from quant_researcher.quant.project_tool.hammer import slim
from quant_researcher.quant.project_tool.celebrity import update_a_dict
from quant_researcher.quant.project_tool.df_tool import single_row_df_to_dict
from quant_researcher.quant.project_tool.time_tool import match_format, date_shifter, get_monthly_sub_intervals
from quant_researcher.quant.project_tool.wrapper_tools import common_wrappers

BRINSON_DATE_FMT = 'YYYY-MM-DD'
FUND_HQ_DATE_COL = 'price_date'

BASE_DYI = {
    '股票': '000985',
    '债券': 'H11001',
    '商品': 'CCFI',
    '现金': 'H11025',
}
BASE_PORTFOLIO = {
    '股票': {'total': 0.25, 'split': [{'ucode': '000985', 'weight': 1, 'a_type': 'index'}]},
    '债券': {'total': 0.25, 'split': [{'ucode': 'H11001', 'weight': 1, 'a_type': 'index'}]},
    '商品': {'total': 0.25, 'split': [{'ucode': 'CCFI', 'weight': 1, 'a_type': 'index'}]},
    '现金': {'total': 0.25, 'split': [{'ucode': 'H11025', 'weight': 1, 'a_type': 'index'}]},
}

PORTFOLIO_DYI = {
    '股票': [('110022', 0.5), ('001938', 0.5)],
    '债券': [('320021', 0.5), ('002534', 0.5)],
    '商品': [('510170', 0.5), ('202027', 0.5)],
    '现金': [('000686', 0.5, 'CURRENCY'), ('000009', 0.5, 'CURRENCY')],
}


# def get_sub_intervals_v2(start, end, fmt='YYYYMMDD', **kwargs):
#     """
#     获取一个日期区间的子区间
#         - 返回开始时间到结束时间之间， 每月最后一天。
#         - 如 [('20200229', '20200331'), ('20200331', '20200412')]
#     :param str start:  开始日期
#     :param str end:  结束日期
#     :param str fmt:  日期格式，默认 YYYYMMDD
#     :param kwargs:
#         - step，str，步长，默认 months
#     :return: list
#     """
#     step = kwargs.pop('step', 'months')
#     LOG.info('%s→%s, 粒度：%s，格式：%s', start, end, step, fmt)
#
#     # 输入的俩时间进行检测
#     match_format(start, fmt)
#     match_format(end, fmt)
#     if start > end:
#         LOG.warning('开始（%s）比结束（%s）大', start, end)
#         return
#     if step == 'months':
#         first_day_of_that_month = start[:-2] + '01'
#         first_day_of_next_month = date_shifter(
#             first_day_of_that_month, step=step, how_many=1, fmt_str=fmt
#         )
#         res = get_monthly_sub_intervals(first_day_of_next_month, end_date=end, step=step, many=1)
#         if res[0][0] > start:
#             res.insert(0, (start, res[0][0]))
#         if res[-1][-1] < end:
#             res.append((res[-1][-1], end))
#
#         hammer.slim(res, name='找到的区间详情')
#         # assert False
#     else:
#         raise ValueError
#     return res
#
#
# def get_w_r(info, sub_intervals, portfolio):
#     """
#     计算组合每一期的各个大类的浮盈
#     :param info: dict，示例如下
#         join，数据join方式：outer
#         fill，数据填充方式：f&b
#     :param sub_intervals: list，
#         ('20160101', '20160201')，('20160201', '20160301')
#     :param portfolio: 组合信息，全局统一格式
#     :return:
#     """
#     last_nav_data = None
#     provided = False
#     share_dict = None
#     weight_dict = None
#     float_profit_of_each_big_df = []
#     weight_of_each_big_df = []
#     for ii in range(len(sub_intervals)):
#         # 得到的是：组合中各小类的行情，各小类的权重及相关信息
#         df, asset_weight = get_all_big(
#             portfolio=portfolio, interval=sub_intervals[ii],
#             how_to_join=info['join'], fill_method=info['fill']
#         )
#         weight_dict = asset_weight if weight_dict is None else weight_dict
#
#         # 把这一期期初的权重记录下来，期末的话，暂不记录
#         # 如果计算某一期，只需要当期期初的权重信息
#         tmp_dict = {'k': ii}
#         for k, v in weight_dict.items():
#             update_a_dict(tmp_dict, v['belong'], v['weight'])
#         weight_of_each_big_df.append(tmp_dict)
#
#         # DEBUG
#         # slim(weight_of_each_big_df)
#         # assert False
#
#         tmp = compute_portfolio_nav_and_chg(
#             df,
#             weight_dict=weight_dict,
#             last_nav_data=last_nav_data,
#             provided_last_nav_data=provided,
#             return_detail=True,
#             share_dict=share_dict
#         )  # 返回值：组合中各小类每日浮盈，组合汇总浮盈+净值+涨跌幅，组合中各小类的份额字典
#         portfolio_hq_df, float_profit_of_each_asset_df, share_dict = tmp
#
#         # 如果是第一个区间呢，就把区间开始的各个小类资产汇总到各自的大类，加入
#         if not provided:
#             the_very_first_money_input = float_profit_of_each_asset_df.head(1)
#             tmp_dict = {'k': ii}
#             for a_col in the_very_first_money_input.columns:
#                 if a_col == 'p_date':
#                     continue
#                 tmp = the_very_first_money_input[a_col].values[0]
#                 update_a_dict(tmp_dict, weight_dict[a_col]['belong'], tmp)
#             float_profit_of_each_big_df.append(tmp_dict)
#
#         provided = True
#         # 期末的组合的总市值（组合浮盈），涨跌幅，拟合净值等信息
#         last_nav_data = single_row_df_to_dict(portfolio_hq_df.tail(1))
#
#         # DEBUG
#         # slim('组合中各个大类的浮盈：')
#         # slim(float_profit_of_each_big_df)
#         # slim(last_nav_data, name='期末信息', a_f=1)
#         # slim(share_dict, name='份额字典', a_f=1)
#
#         # slim(weight_dict, name='期初的权重', a_f=1)  # 配合下面的DEBUG
#         # 计算当期个各个大类的浮盈和各个小类的权重改变之后的最新值
#         tail_of_that_interval = float_profit_of_each_asset_df.tail(1)
#         market_value_of_each_big_dict = {'k': ii+1}
#         for a_col in float_profit_of_each_asset_df.columns:
#             if a_col == 'p_date':
#                 continue
#             tail = tail_of_that_interval[a_col].values[0]
#             update_a_dict(
#                 the_dict=market_value_of_each_big_dict,
#                 key=weight_dict[a_col]['belong'], value=tail
#             )
#
#             # 更新新的各个小类资产的权重，直接使用：小类市值÷组合总市值
#             wei = tail / last_nav_data['float_p']
#             weight_dict[a_col].update({'weight': wei})
#         float_profit_of_each_big_df.append(market_value_of_each_big_dict)
#
#         sum_of_new_weight = sum([x['weight'] for x in weight_dict.values()])
#         LOG.info('更新之后，各类资产权重之和：%s', sum_of_new_weight)
#         if not (math.fabs(1 - sum_of_new_weight) < 0.00000001):
#             LOG.error('权重字典：%s', weight_dict)
#             for weight_dict_k, weight_dict_v in weight_dict.items():
#                 LOG.error('%s, %s', weight_dict_k, weight_dict_v)
#             raise RuntimeError
#
#         # DEBUG
#         # slim(float_profit_of_each_big_df)
#         # slim(weight_dict, name='期末的权重')
#
#     float_profit_of_each_big_df = pd.DataFrame(float_profit_of_each_big_df)
#     weight_of_each_big_df = pd.DataFrame(weight_of_each_big_df)
#
#     float_profit_of_each_big_df.sort_values(by='k', inplace=True)
#     weight_of_each_big_df.sort_values(by='k', inplace=True)
#
#     # DEBUG
#     # slim(float_profit_of_each_big_df, a_f=1)
#     # slim(weight_of_each_big_df, a_f=0)
#
#     return float_profit_of_each_big_df, weight_of_each_big_df
#
#
# def get_portfolio_big_ones_by_portfolio(portfolio):
#     """ 将组合的大类中的小类占各自大类的比例找出来
#     :param portfolio: 组合信息
#     :return: 示例如下
#         {
#             '股票': [('110022', 0.5), ('001938', 0.5)],
#             '债券': [('320021', 0.5), ('002534', 0.5)],
#             '商品': [('510170', 0.5), ('202027', 0.5)],
#             '现金': [('000686', 0.5, 'CURRENCY'), ('000009', 0.5, 'CURRENCY')],
#         }
#     """
#     p_big_ones = {}
#     for k, v in portfolio.items():
#         total = v['total']
#         asset_dict = v['split']
#         res = []
#         for name, weight in asset_dict.items():
#             res.append((name, weight / total if total != 0 else 0))
#         p_big_ones.update({k: res})
#     return p_big_ones
#
#
# @common_wrappers.count_time
# def brinson_main(start, end, portfolio, step=''):
#     """ 计算一个时间段内，按照粒度step划分的每一个子区间的Brinson模型的各个收益
#     :param start: 区间开始
#     :param end: 区间结束
#     :param portfolio: dict，组合信息
#     :param step: 区间粒度，month，year，week，season，
#     :return: 组合每一期的Brinson结果
#     """
#     slim(portfolio, a_f=True)
#
#     # 1. 入参检查：
#     w_sum = 0.0
#     for k, v in portfolio.items():
#         s_sum = sum([x['weight'] for x in v['split']])
#         if s_sum != 1.0 and len(v['split']) != 0:
#             sys_exit('大类：<%s>的子类的权重之和(%s) != 1' % (k, s_sum))
#         w_sum += v['total']
#     if w_sum != 1.0:
#         sys_exit('组合的各个大类的权重之和(%s) != 1' % w_sum)
#     # 开始时间start_date小于结束时间end_date
#     if start >= end:
#         sys_exit('开始时间(%s)小于结束时间(%s)才有意义呀~~' % (start, end))
#     # 开始时间和结束时间是想要的格式
#     match_format(start, BRINSON_DATE_FMT, throw_it=True)
#     match_format(end, BRINSON_DATE_FMT, throw_it=True)
#
#     LOG.info('计算区间内，按照对应的粒度划分的每一个小区间的开始和结束')
#     sub_intervals = [(start, end)]
#     if step:
#         sub_intervals = get_sub_intervals_v2(
#             start, end, step='months', fmt=BRINSON_DATE_FMT
#         )
#     LOG.info('step：%s，因此子区间个数：%s', step, len(sub_intervals))
#     if len(sub_intervals) == 0:
#         return
#
#     LOG.info('拼装Brinson核心模块需要的入参，权重list，各(p, i)和(b, i)的收益率')
#     info = {'join': 'outer', 'fill': 'f&b'}
#     r_p_i, w_p_i = get_w_r(info, sub_intervals, portfolio)
#     r_b_i, w_b_i = get_w_r(info, sub_intervals, BASE_PORTFOLIO)
#
#     LOG.info('调用Brinson核心计算模块')
#     res = brinson(r_p_i, w_p_i, r_b_i, w_b_i, sub_intervals)
#     return res
#
#
# def real_brinson_core(r_p_i, w_p_i, r_b_i, w_b_i):
#     if not (len(r_p_i) == len(w_p_i) == len(r_b_i) == len(w_b_i)):
#         msg = f'len(r_p_i): {len(r_p_i)}, len(w_p_i): {len(w_p_i)},' \
#             f'len(r_b_i): {len(r_b_i)}, len(w_b_i): {len(w_b_i)}'
#         sys_exit(msg)
#     q_4_list = [weight * rtn_rate for weight, rtn_rate in zip(w_p_i, r_p_i)]
#     q_3_list = [weight * rtn_rate for weight, rtn_rate in zip(w_b_i, r_p_i)]
#     q_2_list = [weight * rtn_rate for weight, rtn_rate in zip(w_p_i, r_b_i)]
#     q_1_list = [weight * rtn_rate for weight, rtn_rate in zip(w_b_i, r_b_i)]
#
#     q_4 = sum(q_4_list)
#     q_3 = sum(q_3_list)
#     q_2 = sum(q_2_list)
#     q_1 = sum(q_1_list)
#
#     a_r = q_2 - q_1
#     s_r = q_3 - q_1
#     i_r = q_4 - q_3 - q_2 + q_1
#     t_r = q_4 - q_1
#
#     # a_r_list = [x - y for x, y in zip(q_2_list, q_1_list)]
#     # s_r_list = [x - y for x, y in zip(q_3_list, q_1_list)]
#     # i_r_list = [
#     #     x4 - x3 - x2 + x1 for x1, x2, x3, x4 in
#     #     zip(q_1_list, q_2_list, q_3_list, q_4_list)
#     # ]
#     # t_r_list = [x - y for x, y in zip(q_4_list, q_1_list)]
#     # a_r = sum(a_r_list)
#     # s_r = sum(s_r_list)
#     # i_r = sum(i_r_list)
#     # t_r = sum(t_r_list)
#
#     return {'AR': a_r, 'SR': s_r, 'IR': i_r, 'TR': t_r}
#
#
# def brinson(r_p_i, w_p_i, r_b_i, w_b_i, sub_intervals):
#     # DEBUG
#     # help_dict = {
#     #     'AR': '资产配置收益：AR=Q2-Q1',
#     #     'SR': '个股选择收益：SR=Q3-Q1',
#     #     'IR': '交互收益：IR=Q4-Q3-Q2+Q1',
#     #     'TR': '总超额收益：TR=Q4-Q1=AR+SR+IR',
#     # }
#     # slim(help_dict, a_f=1, name='Brinson收益分解中英对照')
#     # slim(r_p_i, name='r_p_i(before pct_change)', a_f=1)
#     # slim(r_b_i, name='r_b_i(before pct_change)', a_f=1)
#     # slim(w_p_i, name='w_p_i', a_f=1)
#     # slim(w_b_i, name='w_b_i', a_f=1)
#
#     del w_b_i['k']
#     del w_p_i['k']
#     del r_b_i['k']
#     del r_p_i['k']
#
#     r_p_i = r_p_i.pct_change()
#     r_p_i.drop(index=[0], inplace=True)
#     r_p_i.fillna(0, inplace=True)
#
#     r_b_i = r_b_i.pct_change()
#     r_b_i.drop(index=[0], inplace=True)
#     r_b_i.fillna(0, inplace=True)
#
#     # DEBUG
#     # slim(r_p_i, name='r_p_i', a_f=1)
#     # slim(r_b_i, name='r_b_i', a_f=0)
#
#     assert len(r_b_i) == len(w_p_i) == len(w_b_i) == len(r_p_i)
#
#     res = []
#     for idx in range(len(r_b_i)):
#         single_r_p_i = r_p_i.iloc[idx, :].values
#         single_w_p_i = w_p_i.iloc[idx, :].values
#         single_r_b_i = r_b_i.iloc[idx, :].values
#         single_w_b_i = w_b_i.iloc[idx, :].values
#         tmp = real_brinson_core(
#             single_r_p_i, single_w_p_i, single_r_b_i, single_w_b_i
#         )
#         tmp.update({'End': sub_intervals[idx][1]})
#         tmp.update({'Start': sub_intervals[idx][0]})
#         res.append(tmp)
#     return res
#
#
# @common_wrappers.run_from_cmd_wrapper('Brinson')
# def from_cmd(args):
#     start = args.start
#     end = args.end
#     portfolio = args.portfolio.replace('@', '\"')
#     portfolio = json.loads(portfolio)
#     step = args.step
#     return brinson_main(portfolio=portfolio, start=start, end=end, step=step)
#
#
# if __name__ == '__main__':
#     my_p = {
#            '股票': {'total': 1, 'split': [
#                {'ucode': '001423', 'weight': 1, 'a_type': 'fund'},
#            ]},
#            '债券': {'total': 0, 'split': [
#                {'ucode': 'H11001', 'weight': 1, 'a_type': 'index'},
#            ]},
#            '商品': {'total': 0, 'split': [
#                {'ucode': '110011', 'weight': 1, 'a_type': 'fund'}
#            ]},
#            '现金': {'total': 0, 'split': [
#                {'ucode': 'H11025', 'weight': 1, 'a_type': 'index'}
#            ]},
#     }
#
#     an_ans = brinson_main('2018-12-12', '2019-09-23', my_p, step='month')
#     # an_ans = brinson_main('2014-04-01', '2016-03-31', my_p, step='month')
#     slim(pd.DataFrame(an_ans), a_f=1)
